from periphery import GPIO
import subprocess, os


'''GPIO'''
# todo release
def get_LED1():
    LED1 = GPIO("/dev/gpiochip1", 1 * 8 + 1, "out")    # LED1, 开机默认亮, GPIO1_B1
    return LED1

def get_LED2():
    LED2 = GPIO("/dev/gpiochip1", 0 * 8 + 4, "out")    # LED2, 开机默认灭, GPIO1_A4
    return LED2

def get_LASER():
    LASER = GPIO("/dev/gpiochip1", 1 * 8 + 2, "out")   # Laser, GPIO1_B2
    return LASER


class Dial():
    def __init__(self) -> None:
        self.bit0 = GPIO("/dev/gpiochip4", 0 * 8 + 6, "in") # gpio4_A6
        self.bit1 = GPIO("/dev/gpiochip4", 1 * 8 + 5, "in") # gpio4_B5
        self.bit2 = GPIO("/dev/gpiochip4", 1 * 8 + 4, "in") # gpio4_B4
        self.bit3 = GPIO("/dev/gpiochip4", 1 * 8 + 3, "in") # gpio4_B3

    def __del__(self):
        self.close()

    def read(self):
        bit0 = 0b1 if self.bit0.read() else 0
        bit1 = 0b1 if self.bit1.read() else 0
        bit2 = 0b1 if self.bit2.read() else 0
        bit3 = 0b1 if self.bit3.read() else 0

        val = bit0 << 3 | bit1 << 2 | bit2 << 1 | bit3

        return val

    def close(self):
        self.bit0.close()
        self.bit1.close()
        self.bit2.close()
        self.bit3.close()

        

# _banks = (0, 1, 2, 3, 4)
# _groups = ('A', 'B', 'C', 'D')
# def get_gpio(bank, group, x):
#     if not bank in _banks:
#         raise Exception("wrong bank")
#     if not group in _groups:
#         raise Exception("wrong group")
#     if not x in range(0,8):
#         raise Exception("wrong pin")
    
#     chip = f"/dev/gpiochip{bank}"
#     line = 8 * _groups.index(group) + x

#     return (chip, line)


'''pwm'''
class mPWM():
    POLARITY_INVERSED = "inversed"
    POLARITY_NORMAL = "normal"

    def __init__(self, chip, channel):
        self.exported = False

        self._chip =    "/sys/class/pwm/pwmchip{}".format(chip)
        self._channel = "/sys/class/pwm/pwmchip{}/pwm{}".format(chip, channel)

        if not os.path.exists(self._chip):
            raise FileNotFoundError("pwmchip doesn't exist")
        
        self._open()

    def __del__(self):
        # if self.exported:
        #     self.close()
        pass

    def _open(self):
        if not os.path.exists(self._channel): 
            e = subprocess.run("sudo echo 0 > {}/export".format(self._chip), stderr = subprocess.PIPE, shell=True)

            if(e.stderr):
                raise Exception("error when export {}:\n{}".format(self._chip, e.stderr.decode("utf-8")))
        
        c = subprocess.run("sudo chmod 777 {}/*".format(self._channel), stderr = subprocess.PIPE, shell=True)
        if(c.stderr):
            raise Exception("error when chmod {}:\n{}".format(self._channel, c.stderr))
        pass

        self.exported = True

    
    def _write_attr(self, attr, value):
        s = subprocess.run(f"sudo echo {value} > {self._channel}/{attr}", shell = True, stderr = subprocess.PIPE)
        if s.stderr:
            err = s.stderr.decode("utf-8")
            raise Exception(f"error when writing {value} to {self._channel}/{attr}:\n{err}") 
    

    def _read_attr(self, attr):
        s = subprocess.run(f"sudo cat {self._channel}/{attr}", 
                           shell = True, stderr = subprocess.PIPE, stdout = subprocess.PIPE)
        if s.stderr:
            err = s.stderr.decode("utf-8")
            raise Exception(f"error when reading {self._channel}/{attr}:\n{err}") 
        else:
            return s.stdout.decode("utf-8")


    def _set_period(self, peroid_ns: int):
        self._write_attr("period", peroid_ns)

    def _get_peroid(self):
        return int(self._read_attr("period"))
    

    def _set_duty_cycle(self, duty_cycle: int):
        self._write_attr("duty_cycle", duty_cycle)

    def _get_duty_cycle(self):
        return int(self._read_attr("duty_cycle"))


    def _set_polarity(self, polarity):
        if polarity != mPWM.POLARITY_INVERSED and polarity != mPWM.POLARITY_NORMAL:
            return
        
        self._write_attr("polarity", polarity)
    
    def _get_polarity(self):
        return self._read_attr("polarity")
    

    def _set_enable(self, state: int):
        if state == 0 and self.enable:
            self._write_attr("enable", 0)
        
        elif state == 1 and not self.enable:
            self._write_attr("enable", 1)

    def _get_enable(self):
        state = int(self._read_attr("enable"))
        if state == 1:
            return True

        return False
        

    period = property(_get_peroid, _set_period) # period in ns
    duty_cycle = property(_get_duty_cycle, _set_duty_cycle)
    polarity = property(_get_polarity, _set_polarity)
    enable = property(_get_enable, _set_enable)
    

    def close(self):
        if self.exported:
            self.enable = 0
            c = subprocess.run("sudo echo 0 > {}/unexport".format(self._chip), shell = True, stderr = subprocess.PIPE)
            if c.stderr:
                raise Exception("error when unexporting pwm:\n{}".format(c.stderr.decode("utf-8")))
        
            self.exported = False

    def __str__(self):
        return "PWM {}, chip {} (period={} sec, duty_cycle={}, polarity={}, enable={})" \
            .format(self._channel, self._chip, self.period, self.duty_cycle, self.polarity, str(self.enable))


# p = mPWM(0, 0)
# print(p)
# p.period = 20_000_000         # 20ms
# p.duty_cycle = 1_000_000   # 1000us
# p.polarity = mPWM.POLARITY_NORMAL
# p.enable = 1
# print(p)
# while(True):
# 	pass

# import time
# dial = Dial()

# try:
#     while(True):
#         val = dial.read()
#         print(val)
#         print(val & 0b100)
#         print(val & 0b11)
#         print()

#         time.sleep(1)
# except Exception as e:
#     print(e)

# finally:
#     dial.close()
#     print("close")
